package org.jeecg.common.util.poi_tl;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import org.apache.commons.lang3.tuple.Pair;
import org.jeecg.common.util.poi_tl.entity.FunBox;
import org.jeecg.common.util.poi_tl.zzsoftentity.FieldDTO;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@RestController
@RequestMapping("/absfunbox")
public class AbsFunBox {

    private static Pair<String, String> getRequestMapping(AnnotatedElement e) {
        RequestMapping mergedAnnotation = AnnotatedElementUtils.findMergedAnnotation(e, RequestMapping.class);
        String name = Optional.ofNullable(mergedAnnotation).map(x -> {
                    RequestMethod[] method = x.method();
                    RequestMethod requestMethod = ArrayUtil.<RequestMethod>get(method, 0);
                    return requestMethod;
                })
                .map(Enum::name)
                .orElse("");
        String requestMapping = Optional.ofNullable(mergedAnnotation).map(RequestMapping::value).map(x -> x[0]).orElse("");
        return Pair.of(name, requestMapping);
    }

    public static List<FunBox> getAllFun(String controllerPackageName) {
        Class<AbsFunBox> aClass = AbsFunBox.class;
        Set<Class<?>> classes = ClassUtil.scanPackage(controllerPackageName,
                e -> {
                    boolean b = AnnotatedElementUtils.hasAnnotation(e, RequestMapping.class);
                    return b && !StrUtil.equals(aClass.getName(), e.getName());
                });
        return getAllFun(classes);
    }

    private static @NotNull List<FunBox> getAllFun(Set<Class<?>> classes) {
        List<FunBox> collect = classes.stream().flatMap(AbsFunBox::mapToBox).collect(Collectors.toList());
        return collect;
    }
    private static Stream<FunBox> mapToBox(Class<?> e) {
        String controllerRequestMapping = getRequestMapping(e).getRight();
        Method[] declaredMethods = e.getDeclaredMethods();
        return Arrays.stream(declaredMethods)
                .filter(m -> !AnnotatedElementUtils.findAllMergedAnnotations(m, RequestMapping.class).isEmpty())
                .filter(m -> !Modifier.isStatic(m.getModifiers()))
                .map(m -> {
                    Parameter[] parameters = m.getParameters();
                    Pair<String, String> requestMapping2 = getRequestMapping(m);
                    String methodType = requestMapping2.getLeft();
                    String methodRequestMapping = requestMapping2.getRight();
                    FunBox funBox = new FunBox();
                    String s = StrUtil.addPrefixIfNot(controllerRequestMapping, "/");
                    String s1 = StrUtil.addPrefixIfNot(methodRequestMapping, "/");
                    funBox.setRestUrl(StrUtil.concat(false, s, s1));
                    funBox.setMethodType(methodType);
                    String value = m.getAnnotation(ApiOperation.class).value();
                    funBox.setDes(value);
                    funBox.setFunName(m.getName());

                    List<FieldDTO> paramList = getParamsDTO(methodType, m);
                    List<FieldDTO> returnList = getReturnDTO(m);

                    funBox.setParamiter(paramList);
                    funBox.setReturns(returnList);
                    return funBox;
                });
    }

    private static List<FieldDTO> getReturnDTO(Method m) {
        Class<?> returnType = m.getReturnType();
        List<FieldDTO> dtos = genfieldDTO(returnType);
        return dtos;
    }

    private static @NotNull List<FieldDTO> getParamsDTO(String methodType, Method method) {
        Parameter[] parameters = method.getParameters();
        List<FieldDTO> paramList = new ArrayList<>();

        if (methodType.equalsIgnoreCase("GET")) {
            for (Parameter parameter : parameters) {
                FieldDTO fieldDto = new FieldDTO();
                ApiModelProperty annotation = parameter.getAnnotation(ApiModelProperty.class);
                if (Objects.nonNull(annotation)) {
                    String value1 = annotation.value();
                    fieldDto.setFieldName(value1);
                }
                fieldDto.setFieldEng(parameter.getName());
                fieldDto.setFieldType(parameter.getType().getSimpleName());
                paramList.add(fieldDto);
            }
        } else if (methodType.equalsIgnoreCase("POST")) {
            for (Parameter parameter : parameters) {
                Class<?> aClass = (Class<?>) parameter.getParameterizedType();
                List<FieldDTO> dtos = genfieldDTO(aClass);
                paramList.addAll(dtos);
            }
        }
        return paramList;
    }

    private static List<FieldDTO> genfieldDTO(Class<?> aClass) {
        Field[] fields = ReflectUtil.getFields(aClass, f -> {
            boolean b = !(f.getModifiers() == Modifier.STATIC);
//            boolean annotationPresent = f.isAnnotationPresent(ApiModelProperty.class);
            return b;
        });
        List<FieldDTO> collect = Arrays.stream(fields).map(f -> {
                    ApiModelProperty annotation = f.getAnnotation(ApiModelProperty.class);
                    String name = f.getName();
                    String comment = Optional.ofNullable(annotation).map(ApiModelProperty::value).filter(StrUtil::isNotBlank).orElse(name);
                    FieldDTO fieldDTO = new FieldDTO();
                    fieldDTO.setFieldName(comment);
                    fieldDTO.setFieldEng(name);
                    String simpleName = f.getType().getSimpleName();
                    fieldDTO.setFieldType(simpleName);
                    boolean equals = simpleName.equals("String");
                    fieldDTO.setFieldLong(equals ? "255" : "0");
                    return fieldDTO;
                })
                .distinct()
                .collect(Collectors.toList());
        return collect;
    }

    @SneakyThrows
    private static String buildJsonData(Parameter[] parameters) {
        String jsonString = JSON.toJSONString(parameters);

//        String jsonString = JSON.toJSONString(paramMap);
        return jsonString;
    }

}